/*
 *  kernel/kernel.c
 *
 *  Minikernel. Versi�n 1.0
 *
 *  Fernando P�rez Cotoya
 *
 */

/*
 *
 * Fichero que contiene la funcionalidad del sistema operativo
 *
 */

#include "kernel.h"	/* Contiene defs. usadas por este modulo */
#include <stdio.h>
#include <string.h>

/*
 *
 * Funciones relacionadas con la tabla de procesos:
 *	iniciar_tabla_proc buscar_BCP_libre
 *
 */

/*
 * Funci�n que inicia la tabla de procesos
 */
static void iniciar_tabla_proc() {
    int i=0;
    
    for (; i < MAX_PROC; i++)
        tabla_procs[i].estado = NO_USADA;
    
}

/*Inicializa los valores de la tabla de mutex*/
void inicializarMutex(){
    int i= 0;
    
    for (; i < NUM_MUT; i++) {
        tabla_mutex[i].id_proceso_bloqueante = -1;
        tabla_mutex[i].estado = UNLOCK;
        tabla_mutex[i].num_bloqueos=0;
    }
}
/*
 * Funci�n que busca una entrada libre en la tabla de procesos
 */
static int buscar_BCP_libre() {
    int i=0;
    
    for (; i < MAX_PROC; i++)
        if (tabla_procs[i].estado == NO_USADA)
            return i;
    return -1;
}

/*
 *
 * Funciones que facilitan el manejo de las listas de BCPs
 *	insertar_ultimo eliminar_primero eliminar_elem
 *
 * NOTA: PRIMERO SE DEBE LLAMAR A eliminar Y LUEGO A insertar
 */

/*
 * Inserta un BCP al final de la lista.
 */
static void insertar_ultimo(lista_BCPs *lista, BCP * proc) {
    
    if (lista->primero == NULL) {
        lista->primero = proc;
    } else {
        lista->ultimo->siguiente = proc;
        
    }
    lista->ultimo = proc;
    proc->siguiente = NULL;
    
}

/*
 * Inserta un BCP de forma ordenada segun los ticks de dormido que tiene que
 * en la lista de dormidos de t
 */
static void insertar_dormido(BCP * proc) {
    BCP * anterior_proceso = NULL;
    BCP * siguiente_proceso = lista_dormidos.primero;
    
    if (lista_dormidos.primero == NULL) {
        lista_dormidos.primero = proc;
        lista_dormidos.ultimo = proc;
        lista_dormidos.ultimo->siguiente = NULL;
        
    } else {
        
        while (siguiente_proceso != NULL && (siguiente_proceso->tick_dormido <= proc->tick_dormido)) {
            anterior_proceso = siguiente_proceso;
            siguiente_proceso = anterior_proceso->siguiente;
        }
        
        if (anterior_proceso != NULL) {
            anterior_proceso->siguiente = proc;
            proc->siguiente = siguiente_proceso;
        } else {
            proc->siguiente = siguiente_proceso;
            lista_dormidos.primero = proc;
        }
        
        if (siguiente_proceso == NULL)
            lista_dormidos.ultimo = proc;
        
    }
    
    
}

/*
 * Inserta un BCP al final de la lista.
 */
static void insertar_media(lista_BCPs *lista, BCP * proc) {
    BCP * anterior_proceso = NULL;
    BCP * siguiente_proceso = NULL;
    
    if (lista->primero == NULL) {
        lista->primero = proc;
        
    } else {
        anterior_proceso = (BCP *) buscar_posicion(lista, proc);
        
        if (anterior_proceso == NULL) {
            siguiente_proceso = lista->primero;
            lista->primero = proc;
            lista->primero->siguiente = siguiente_proceso;
        } else {
            siguiente_proceso = anterior_proceso->siguiente;
            anterior_proceso->siguiente = proc;
            proc->siguiente = siguiente_proceso;
        }
    }
    
    if (siguiente_proceso == NULL) {
        lista->ultimo = proc;
        lista->ultimo->siguiente = NULL;
    }
    
}

static void insertar_cola_prioridad(BCP * proceso) {
    if (proceso!=NULL){
        switch (proceso->prioridad) {
            
            case 0:
                insertar_ultimo(&lista_alta, proceso);
                break;
            case 1:
                insertar_media(&lista_media, proceso);
                break;
            case 2:
                insertar_ultimo(&lista_baja, proceso);
                break;
            default:
                printk("ERROR INSERTAR COLA PRIORIDAD");
        }
    }
    
    
}

/*
 * Elimina el primer BCP de la lista.
 */
static void eliminar_primero(lista_BCPs *lista) {
    
    if (lista->ultimo == lista->primero)
        lista->ultimo = NULL;
    lista->primero = lista->primero->siguiente;
}

/*
 * Elimina un determinado BCP de la lista.
 */
static void eliminar_elem(lista_BCPs *lista, BCP * proc) {
    BCP *paux = lista->primero;
    
    if (paux == proc) {
        eliminar_primero(lista);
    } else {
        for (; ((paux) && (paux->siguiente != proc));
        paux = paux->siguiente);
        if (paux) {
            if (lista->ultimo == paux->siguiente)
                lista->ultimo = paux;
            paux->siguiente = paux->siguiente->siguiente;
        }
    }
}

/*Metodo que obtiene el primer elemento de una lista y lo elimina de la misma*/

static BCP * obtener_primero(lista_BCPs *lista) {
    BCP * primero = lista->primero;
    eliminar_primero(lista);
    
    return primero;
}


/*
 *
 * Funciones relacionadas con la planificacion
 *	espera_int planificador
 */

/*
 * Espera a que se produzca una interrupcion
 */
static void espera_int() {
    int nivel;
    
    // printk("->NO HAY LISTOS. ESPERA INT\n");
    
    /* Baja al m�nimo el nivel de interrupci�n mientras espera */
    nivel = fijar_nivel_int(NIVEL_1);
    
    halt();
    
    fijar_nivel_int(nivel);
    
}

/*
 * Funci�n de planificacion que implementa un algoritmo FIFO.
 */
static BCP * planificador_init() {
    while (lista_alta.primero == NULL)
        espera_int(); /* No hay nada que hacer */
    return lista_alta.primero;
}

/*
 * Funcion de planificacion con prioridad. Tambien planifica los estados bloqueados
 */
static BCP * planificador_prioridad() {
    BCP * proceso_ejecucion = p_proc_actual;
    
    int nivel=fijar_nivel_int(NIVEL_3);
    
    /*Obtencion de proceso viniendo de espera*/
    if (p_proc_actual == NULL) {
        
        if (lista_alta.primero != NULL)
            proceso_ejecucion = planificador_alto();
        else if (lista_media.primero != NULL)
            proceso_ejecucion = planificador_medio();
        else if (lista_baja.primero != NULL)
            proceso_ejecucion = planificador_bajo();
        
        /*Obtencion de proceso viniendo de ejcecucion de un proceso*/
    } else {
        
        if (lista_alta.primero != NULL || (p_proc_actual->prioridad == P_ALTO && p_proc_actual->estado != TERMINADO && p_proc_actual->estado != DORMIDO && p_proc_actual->estado != BLOQUEADO))
            proceso_ejecucion = planificador_alto();
        else if (lista_media.primero != NULL || (p_proc_actual->prioridad == P_MEDIO && p_proc_actual->estado != TERMINADO && p_proc_actual->estado != DORMIDO && p_proc_actual->estado != BLOQUEADO))
            proceso_ejecucion = planificador_medio();
        else if (lista_baja.primero != NULL || (p_proc_actual->prioridad == P_BAJO && p_proc_actual->estado != TERMINADO && p_proc_actual->estado != DORMIDO && p_proc_actual->estado != BLOQUEADO))
            proceso_ejecucion = planificador_bajo();
    }
    
    /*Comprobacion de si no hay que ejecutar ningun proceso*/
    if (lista_alta.primero == NULL &&
    lista_media.primero == NULL &&
    lista_baja.primero == NULL &&
    (proceso_ejecucion == NULL || proceso_ejecucion->estado == TERMINADO || proceso_ejecucion->estado == DORMIDO || proceso_ejecucion->estado == BLOQUEADO)) {
        
        proceso_ejecucion = p_proc_actual = NULL;
        
    }
    
    fijar_nivel_int(nivel);
    return proceso_ejecucion;
}

/*
 * Funci�n de planificacion que implementa un algoritmo FIFO.
 */
static BCP * planificador_alto() {
    if (p_proc_actual != NULL && p_proc_actual->prioridad != P_ALTO && p_proc_actual->estado != TERMINADO && p_proc_actual->estado != DORMIDO && p_proc_actual->estado != BLOQUEADO) {
        p_proc_actual->numero_expulsiones++;
        insertar_cola_prioridad(p_proc_actual);
    }
    
    //imprimir_listas();
    return obtener_primero(&lista_alta);
}

/*
 * Funci�n de planificacion que implementa un algoritmo de expulsion por numero de expulsiones.
 */
static BCP * planificador_medio() {
    if (p_proc_actual != NULL && p_proc_actual->estado != TERMINADO && p_proc_actual->estado != DORMIDO && p_proc_actual->estado != BLOQUEADO) {
        p_proc_actual->numero_expulsiones++;
        insertar_cola_prioridad(p_proc_actual);
    }
    //imprimir_listas();
    return obtener_primero(&lista_media);
}

/*Funcion que implementa un algoritmo de expulsion de round robin
 */
static BCP * planificador_bajo() {
    
    if (p_proc_actual != NULL && p_proc_actual->estado != TERMINADO && p_proc_actual->estado != DORMIDO && p_proc_actual->estado != BLOQUEADO) {
        p_proc_actual->numero_expulsiones++;
        insertar_cola_prioridad(p_proc_actual);
    }
    
    //imprimir_listas();
    return obtener_primero(&lista_baja);
}

/*Imprime el estado de las Listas en el instante actual*/

void imprimir_listas() {
    BCP * procesoAux = lista_alta.primero;
    printk("\n\nCola de Procesos de Prioridad Alta\n---------------------------\n\n");
    
    if (procesoAux == NULL)
        printk("\n\n\tNINGUN PROCESO\n\n");
    
    while (procesoAux != NULL) {
        
        printk("Proceso (%d)\n", procesoAux->id);
        procesoAux = procesoAux->siguiente;
    }
    
    
    printk("\n\nCola de Procesos de Prioridad Media\n---------------------------\n\n");
    procesoAux = lista_media.primero;
    
    if (procesoAux == NULL)
        printk("\n\n\tNINGUN PROCESO\n\n");
    
    while (procesoAux != NULL) {
        
        printk("Proceso (%d)\n", procesoAux->id);
        procesoAux = procesoAux->siguiente;
    }
    
    printk("\n\nCola de Procesos de Prioridad Baja\n---------------------------\n\n");
    procesoAux = lista_baja.primero;
    
    if (procesoAux == NULL)
        printk("\n\n\tNINGUN PROCESO\n\n");
    
    while (procesoAux != NULL) {
        printk("Proceso (%d)\n", procesoAux->id);
        procesoAux = procesoAux->siguiente;
    }
    
    printk("\n\nCola de Procesos de Bloqueados Por Teclado\n---------------------------\n\n");
    procesoAux = lista_bloqueados_teclado.primero;
    
    if (procesoAux == NULL)
        printk("\n\n\tNINGUN PROCESO\n\n");
    
    while (procesoAux != NULL) {
        
        printk("Proceso (%d)\n", procesoAux->id);
        procesoAux = procesoAux->siguiente;
    }
    
    printk("\n\nCola de Procesos de Dormidos\n---------------------------\n\n");
    procesoAux = lista_dormidos.primero;
    
    if (procesoAux == NULL)
        printk("\n\n\tNINGUN PROCESO\n\n");
    
    while (procesoAux != NULL) {
        
        printk("Proceso (%d)\n", procesoAux->id);
        procesoAux = procesoAux->siguiente;
    }
    printk("\n\n\n\n");
}

/*Imprime la situcacion de los Mutex*/

void imprimir_listas_mutex() {
    BCP * procesoAux = NULL;
    int i = 0;
    
    for (; i < numero_mutex; i++) {
        printk("\n\nCola de Bloqueados por el mutex (%d)\n---------------------------\n\n", i);
        procesoAux = lista_bloqueados_mutex[i].primero;
        if (procesoAux == NULL)
            printk("\n\n\tNINGUN PROCESO\n\n");
        
        while (procesoAux != NULL) {
            
            printk("Proceso (%d)\n", procesoAux->id);
            procesoAux = procesoAux->siguiente;
        }
        
    }
    
}

/*Imprime la informacion de un mutex*/
void imprimir_mutex() {
    int i = 0;
    for (; i < numero_mutex; i++) {
        printk("->\n\n\nMutex (%d).....\n", (i + 1));
        printk("->nombre mutex (%s).....\n", tabla_mutex[i].nombre_mutex);
        printk("->estado mutex (%d).....\n", tabla_mutex[i].estado);
        printk("->id mutex (%d).....\n", tabla_mutex[i].id_mutex);
        printk("->id bloqueandte mutex (%d).....\n", tabla_mutex[i].id_proceso_bloqueante);
        printk("->tipo mutex (%d).....\n\n\n", tabla_mutex[i].tipo_mutex);
    }
}

/*
 *
 * Funcion auxiliar que termina proceso actual liberando sus recursos.
 * Usada por llamada terminar_proceso y por rutinas que tratan excepciones
 *
 */
static void liberar_proceso() {
    BCP * p_proc_anterior;
    liberarMutexProceso(p_proc_actual);
    liberar_imagen(p_proc_actual->info_mem); /* liberar mapa */
    p_proc_actual->estado = TERMINADO;
    
    
    /*comprobacion de proceso inicial*/
    if (p_proc_actual->id == 0 && expulsado_init == 0) {
        eliminar_primero(obtener_lista(p_proc_actual)); /* proc. fuera de listos */
        expulsado_init = 1;
    }
    
    /* Realizar cambio de contexto */
    p_proc_anterior = p_proc_actual;
    p_proc_actual = planificador_prioridad();
    
    /*Cambio por el siguiente proceso ha ejecutar*/
    if (p_proc_actual != NULL) {
        
        printk("->C.CONTEXTO POR FINALIZACION: de %d a %d\n", p_proc_anterior->id, p_proc_actual->id);
        liberar_pila(p_proc_anterior->pila);
        numero_ticks_proceso = 0;
        cambio_contexto(NULL, &(p_proc_actual->contexto_regs));
        
        /*No hay nuevo proceso ha ejecutar pero quedan procesos bloqueados*/
    } else if (!noHayMasProcesos()){
        printk("->C.CONTEXTO POR FINALIZACION: de %d a NULL\n", p_proc_anterior->id);
        liberar_pila(p_proc_anterior->pila);
        numero_ticks_proceso = 0;
        cambio_contexto(NULL, NULL);
        
        /*Esperar mientras haya procesos bloqueados*/
        while (!noHayMasProcesos()) {
            espera_int();
        }
        
        /*Ya no quedan mas procesos por ejecutar*/
    }else{
        printk("->No hay mas procesos\n");
        
    }
    
    return; /* no deber�a llegar aqui */
}

/*
 * Metodo que comprueba si queda algun proceso bloqueado en las listas auxiliares
 */
int noHayMasProcesos() {
    int i=0;
    if (lista_dormidos.primero==NULL && lista_bloqueados_teclado.primero==NULL){
        for (;i<NUM_MUT&&lista_bloqueados_mutex[i].primero==NULL;i++);
        
        if(i==NUM_MUT)
            return 1;
    }
    
    return 0;
}

/*
 * Metodo que busca entre que proceso de la cola de prioridad hay que insertar el nuevo proceso
 */
BCP* buscar_posicion(lista_BCPs * lista, BCP * proc) {
    BCP * proceso_actual = lista->primero;
    BCP * proceso_anterior = NULL;
    while ((proceso_actual != NULL) && (proceso_actual->numero_expulsiones >= proc->numero_expulsiones)) {
        proceso_anterior = proceso_actual;
        proceso_actual = proceso_anterior->siguiente;
    }
    
    return proceso_anterior;
}

/*Obtiene la lista de prioridad asociada a un proceso*/
lista_BCPs * obtener_lista(BCP * proceso) {
    switch (proceso->prioridad) {
        case 0:
            return &lista_alta;
        case 1:
            return &lista_media;
        case 2:
            return &lista_baja;
        default:
            printk("ERROR OBTENIENDO LA LISTA DE PRIORIDAD\n");
    }
    return NULL;
}

/*
 *
 * Funciones relacionadas con el tratamiento de interrupciones
 *	excepciones: exc_arit exc_mem
 *	interrupciones de reloj: int_reloj
 *	interrupciones del terminal: int_terminal
 *	llamadas al sistemas: llam_sis
 *	interrupciones SW: int_sw
 *
 */

/*
 * Tratamiento de excepciones aritmeticas
 */
static void exc_arit() {
    if (!viene_de_modo_usuario())
        panico("excepcion aritmetica cuando estaba dentro del kernel\n");
    
    
    printk("->EXCEPCION ARITMETICA EN PROC %d\n", p_proc_actual->id);
    liberar_proceso();
    
    return; /* no deber�a llegar aqui */
}

/*
 * Tratamiento de excepciones en el acceso a memoria
 */
static void exc_mem() {
    if (!viene_de_modo_usuario()) panico("excepcion de memoria cuando estaba dentro del kernel");
    printk("->EXCEPCION DE MEMORIA EN PROC %d\n", p_proc_actual->id);
    
    liberar_proceso();
    
    return; /* no deber�a llegar aqui */
}

/*
 * Tratamiento de interrupciones de terminal
 */
static void int_terminal() {
    
    char caracter = leer_puerto(DIR_TERMINAL);
    BCP * p_proc_anterior;/* Variable auxiliar para hacer el cambio de contexto*/
    
    //comprueba si hay espacio para meter mas caracter en el buffer
    if (numero_caracteres < TAM_BUF_TERM) {
        printk("->Caracter insertado  %c\n", caracter);
        buffer_teclado[numero_caracteres] = caracter;
        numero_caracteres++;
        desbloquearProcesoTeclado();
        p_proc_anterior = p_proc_actual;
        p_proc_actual = planificador_prioridad();
        
        //imprimir_listas();
        if(p_proc_anterior==NULL||(p_proc_anterior!=NULL&&p_proc_anterior->id!=p_proc_actual->id)){
            numero_ticks_proceso = 0;
            
            if (p_proc_actual != NULL) {
                if (p_proc_anterior != NULL) {
                    printk("->C.CONTEXTO: de (%d) a (%d)\n", p_proc_anterior->id, p_proc_actual->id);
                    cambio_contexto(&(p_proc_anterior->contexto_regs), &(p_proc_actual->contexto_regs));
                } else {
                    printk("->C.CONTEXTO Nulo : de Null a (%d)\n", p_proc_actual->id);
                    cambio_contexto(NULL, &(p_proc_actual->contexto_regs));
                }
            }
        }
        
    }
    
    return;
}

/*
 * Tratamiento de interrupciones de reloj
 */
static void int_reloj() {
    BCP * p_proc_anterior;/* Variable auxiliar para hacer el cambio de contexto*/
    // printk("->INTERRUPCION DE RELOJ\n");
    
    //printk("->TICKS GLOBALES %d\n", numero_ticks_global);
    
    //Bloquea con nivel 3 la posible ejecucion de otras interrupciones
    int nivel=fijar_nivel_int(NIVEL_3);
    
    //Comprueba si hay algun proceso dormido que le toque despertarse
    int despertar = comprobar_dormidos();
    
    
    //Incrementa el numero de ticks de reloj ejecutado por el kernel y por el proceso en ejecucion
    numero_ticks_global++;
    numero_ticks_proceso++;
    
    //Comprueba si un proceso ha acabado su rodaja de tiempo
    if (p_proc_actual == NULL || ((p_proc_actual->prioridad == P_BAJO && numero_ticks_proceso == TICKS_POR_RODAJA_P_BAJA) ||
            (p_proc_actual->prioridad == P_MEDIO && numero_ticks_proceso == TICKS_POR_RODAJA) ||
            (p_proc_actual->prioridad == P_ALTO && p_proc_actual->estado == TERMINADO) ||
            despertar)) {
        
        p_proc_anterior = p_proc_actual;
        p_proc_actual = planificador_prioridad();
        
        //Comprueba si hace falta reiniciar el numero de tiempo de cpu del proceso
        if (p_proc_actual == NULL || (p_proc_anterior != p_proc_actual ||
                !despertar ||
                ((p_proc_actual->prioridad == P_BAJO && numero_ticks_proceso == TICKS_POR_RODAJA_P_BAJA) ||
                (p_proc_actual->prioridad == P_MEDIO && numero_ticks_proceso == TICKS_POR_RODAJA) ||
                (p_proc_actual->prioridad == P_ALTO && p_proc_actual->estado == TERMINADO)))) {
            
            numero_ticks_proceso = 0;
            
        }
        
        
        if (p_proc_actual != NULL) {
            
            if (p_proc_anterior != NULL) {
                printk("->C.CONTEXTO: de (%d) a (%d)\n",
                        p_proc_anterior->id, p_proc_actual->id);
                
                //Desbloquea el nivel de interrupcion 3 y lo pone en el nivel en el que se encontraba antes del bloqueo
                fijar_nivel_int(nivel);
                cambio_contexto(&(p_proc_anterior->contexto_regs), &(p_proc_actual->contexto_regs));
            } else {
                
                printk("->C.CONTEXTO Nulo : de Null a (%d)\n", p_proc_actual->id);
                
                //Desbloquea el nivel de interrupcion 3 y lo pone en el nivel en el que se encontraba antes del bloqueo
                fijar_nivel_int(nivel);
                cambio_contexto(NULL, &(p_proc_actual->contexto_regs));
            }
            
        }
    }
    
    //Desbloquea el nivel de interrupcion 3 y lo pone en el nivel en el que se encontraba antes del bloqueo
    fijar_nivel_int(nivel);
    return;
    
    
    
    
}

/*Funcion que comprueba si toca despertar a algun proceso dormido*/
int comprobar_dormidos() {
    int despertar = 0; /*variable que comprueba si en alguna de las iteraciones no se despierta a nadie mas*/
    
    int despertado = 0; /*valor de devolucion que indica si se ha despertado a  algun proceso*/
    
    BCP * procesoAux = lista_dormidos.primero; /*variable auxiliar para comprobar si hay algun proceso para despertar*/
    
    //  BCP * procesoSiguiente = NULL; /*variable auxiliar 2 para comprobar si hay algun proceso para despertar*/
    
    while (despertar == 0 && procesoAux != NULL) {
        
        //If que comprueba si hay que despertar al proceso
        if (procesoAux->tick_dormido <= numero_ticks_global) {
            
            //Despertar proceso
            procesoAux->tick_dormido = 0;
            procesoAux->estado = LISTO;
            //          procesoSiguiente = procesoAux->siguiente;
            
            //insertar el proceso despertado en su cola de prioridad
            insertar_cola_prioridad(obtener_primero(&lista_dormidos));
            
            //Modificar la cola de dormidos
            // eliminar_primero(lista_dormidos);
            /*lista_dormidos.primero = procesoSiguiente;
             * if (lista_dormidos.primero == NULL)
             * lista_dormidos.ultimo = NULL;*/
            
            
            printk("->PROCESO (%d) DESPERTADO\n", procesoAux->id);
            procesoAux = lista_dormidos.primero;
            
            
            
            despertado = 1;
        } else
            //ya no hay mas procesos que despertar
            despertar = 1;
        
        
    }
    //imprimir_listas();
    return despertado;
    
}

/*
 * Tratamiento de llamadas al sistema
 */
static void tratar_llamsis() {
    int nserv, res;
    
    nserv = leer_registro(0);
    if (nserv < NSERVICIOS)
        res = (tabla_servicios[nserv].fservicio)();
    else
        res = -1; /* servicio no existente */
    escribir_registro(0, res);
    return;
}

/*
 * Tratamiento de interrupciuones software
 */
static void int_sw() {
    printk("->TRATANDO INT. SW\n");
    
    return;
}

/*
 *
 * Funcion auxiliar que crea un proceso con prioridad reservando sus recursos.
 * Usada por llamada crear_proceso_prioridad.
 *
 */
static int crear_tarea_prioridad(char *prog, int prioridad) {
    void * imagen, *pc_inicial;
    int error = 0;
    int proc;
    BCP *p_proc;
    
    proc = buscar_BCP_libre();
    if (proc == -1)
        return -1; /* no hay entrada libre */
    
    /* A rellenar el BCP ... */
    p_proc = &(tabla_procs[proc]);
    
    /* crea la imagen de memoria leyendo ejecutable */
    imagen = crear_imagen(prog, &pc_inicial);
    if (imagen) {
        p_proc->info_mem = imagen;
        p_proc->pila = crear_pila(TAM_PILA);
        fijar_contexto_ini(p_proc->info_mem, p_proc->pila, TAM_PILA,
                pc_inicial,
                &(p_proc->contexto_regs));
        p_proc->id = proc;
        p_proc->padre = p_proc_actual;
        p_proc->numero_expulsiones = 0;
        p_proc->estado = LISTO;
        p_proc->prioridad = prioridad;
        inicializarTablaMutex(p_proc);
        /* lo inserta al final de cola de listos */
        switch (prioridad) {
            case 0:
                insertar_ultimo(&lista_alta, p_proc);
                break;
            case 1:
                insertar_media(&lista_media, p_proc);
                break;
            case 2:
                insertar_ultimo(&lista_baja, p_proc);
                break;
            default:
                return -2; // Fallo al insertar la prioridad
        }
        error = 0;
    } else
        error = -1; /* fallo al crear imagen */
    return error;
}
/*Pone todos los valores del arry de mutex a -1*/
void inicializarTablaMutex(BCP * proceso){
    int i=0;
    for(;i<NUM_MUT;i++) {
        proceso->mutex_abiertos[i]=-1;
    }
}
/*Busca la primera entrada libre del array de mutex*/
int buscar_entrada_mutex_libre(char * nombre_nuevo) {
    //printk("voy a ejecutar buscar_entrada_mutex_libre\n");
    int i = 0;
    for (; i < numero_mutex; i++) {
        if (strcmp(nombre_nuevo, tabla_mutex[i].nombre_mutex) == 0)
            return -1;
    }
    return numero_mutex;
}

/*Añade el mutex al array de mutex abiertos del proceso*/
int anyadirMutexAbierto(BCP * proceso, int id) {
    
    int i = 0;
    for (; i < NUM_MUT; i++) {
        if (proceso->mutex_abiertos[i]==-1){
            proceso->mutex_abiertos[i]=id;
            return 1;
        }
    }
    
    return -1;
}
/*Comprueba si el Mutex ha si abierto por el proceso antes*/
int comprobarMutexAbierto(BCP * proceso, int id) {
    int i = 0;
    for (; i < NUM_MUT; i++) {
        if (proceso->mutex_abiertos[i]==id)
            return 1;
    }
    return 0;
}

/*Elimina el Mutex del array de mutex abiertos por el proceso*/
int eliminarMutexAbierto(BCP * proceso, int id) {
    int i = 0;
    for (; i < NUM_MUT; i++) {
        if (proceso->mutex_abiertos[i]==id) {
            proceso->mutex_abiertos[i]=-1;
            return 1;
        }
    }
    return -1;
}
/*Comprueba si el mutex existe*/
int isMutexExiste(int id) {
    return id<=numero_mutex;
}

/*libera los mutex que han sido bloqueados por un proceso*/
void liberarMutexProceso(BCP * proceso) {
    int i = 0;
    printk("->PROCESO (%d): LIBERANDO SUS MUTEX BLOQUEADOS\n", proceso->id);
    
    for (; i < NUM_MUT; i++) {
        if(tabla_mutex[i].id_proceso_bloqueante==proceso->id) liberarMutex(i);
    }
    
    //imprimir_listas_mutex();
}

/*libera un mutex concreto*/
void liberarMutex(int desc) {
    
    // BCP * procesoAux = NULL; /*variable auxiliar para comprobar si hay algun proceso para desbloquear*/
    //BCP * procesoSiguiente = NULL; /*variable auxiliar 2 para comprobar si hay algun proceso para desbloquear*/
    
    if (tabla_mutex[desc].id_proceso_bloqueante==p_proc_actual->id) {
        printk("->PROCESO (%d): LIBERANDO EL MUTEX (%s)\n", p_proc_actual->id, tabla_mutex[desc].nombre_mutex);
        
        if(lista_bloqueados_mutex[desc].primero != NULL) {
            tabla_mutex[desc].num_bloqueos=1;
            tabla_mutex[desc].id_proceso_bloqueante = lista_bloqueados_mutex[desc].primero->id;
            lista_bloqueados_mutex[desc].primero->estado = LISTO;
            insertar_cola_prioridad(obtener_primero(&lista_bloqueados_mutex[desc]));
            
            //Modificar la cola de bloqueados
            //eliminar_primero(lista_bloqueados_mutex[desc])
            /*procesoAux = lista_bloqueados_mutex[desc].primero;
             * procesoSiguiente = procesoAux->siguiente;
             * lista_bloqueados_mutex[desc].primero = procesoSiguiente;
             * if (lista_bloqueados_mutex[desc].primero == NULL)
             * lista_bloqueados_mutex[desc].ultimo = NULL;*/
            
        } else {
            
            tabla_mutex[desc].id_proceso_bloqueante = -1;
            tabla_mutex[desc].estado = UNLOCK;
            tabla_mutex[desc].num_bloqueos=0;
        }
        
    }
    
}

void desbloquearProcesoTeclado() {
    // BCP * procesoAux = NULL; /*variable auxiliar para comprobar si hay algun proceso para desbloquear*/
    //BCP * procesoSiguiente = NULL; /*variable auxiliar 2 para comprobar si hay algun proceso para desbloquear*/
    
    if(lista_bloqueados_teclado.primero != NULL) {
        lista_bloqueados_teclado.primero->estado = LISTO;
        insertar_cola_prioridad(p_proc_actual);
        insertar_cola_prioridad(obtener_primero(&lista_bloqueados_teclado));
    }
}
/*
 *
 * Rutinas que llevan a cabo las llamadas al sistema
 *	sis_crear_proceso sis_escribir
 *
 */

/*
 * Tratamiento de llamada al sistema crear_proceso. Llama a la
 * funcion auxiliar crear_tarea sis_terminar_proceso
 */
int sis_crear_proceso() {
    char *prog;
    int res;
    
    prog = (char *) leer_registro(1);
    res = crear_tarea_prioridad(prog, P_MEDIO);
    printk("->PROC %d: CREAR PROCESO NORMAL DE TIPO %s CON PRIORIDAD %d\n", p_proc_actual->id, prog, P_MEDIO);
    return res;
}

/*
 * Tratamiento de llamada al sistema escribir. Llama simplemente a la
 * funcion de apoyo escribir_ker
 */
int sis_escribir() {
    char *texto;
    unsigned int longi;
    
    texto = (char *) leer_registro(1);
    longi = (unsigned int) leer_registro(2);
    
    escribir_ker(texto, longi);
    return 0;
}

/*
 * Tratamiento de llamada al sistema terminar_proceso. Llama a la
 * funcion auxiliar liberar_proceso
 */
int sis_terminar_proceso() {
    printk("->FIN PROCESO %d\n", p_proc_actual->id);
    liberar_proceso();
    
    return 0; /* no deber�a llegar aqui */
}

/*
 * Tratamiento de la llamada al sistema obtener_id. Obtiene un puntero al BCP
 * actual del que retorna su atributo id
 *
 */
int sis_obtener_id() {
    return p_proc_actual->id;
}

/*
 * Tratamiento de la llamada al sistema obtener_id_padre. Obtiene un puntero al BCP
 * actual del que retorna su atributo id_padre
 *
 */
int sis_obtener_id_padre() {
    int id_p = p_proc_actual->padre->id;
    
    if (id_p >= 0)
        return id_p;
    else
        return -1;
}

/*
 * Tratamiento de la llamada al sistema obtener_numero_expulsiones. Obtiene un puntero al BCP
 * actual del que retorna su atributo numero_expulsiones
 *
 */
int sis_obtener_numero_expulsiones() {
    return p_proc_actual->numero_expulsiones;
}

/*
 * Tratamiento de la llamada al sistema dormir. Esta llamada bloquea durante un perido de tiempo el proceso actual
 *
 */
int sis_dormir() {
    BCP * p_proc_anterior = NULL;
    unsigned int segundos;
    
    segundos = (unsigned int) leer_registro(1);
    //Configura el proceso actual para dormirlo
    p_proc_actual->estado = DORMIDO;
    p_proc_actual->tick_dormido = (segundos * 100) + numero_ticks_global;
    
    printk("PROCESO (%d) DORMIDO HASTA LA RODAJA DE TIEMPO (%d)\n\n", p_proc_actual->id, p_proc_actual->tick_dormido);
    
    //inserta el proceso actual en la lista de dormidos
    insertar_dormido(p_proc_actual);
    eliminar_elem(obtener_lista(p_proc_actual), p_proc_actual);
    
    //imprimir_listas();
    
    //Cambia el proceso actual de ejecucion por el siguiente a ejecutarse
    numero_ticks_proceso = 0;
    p_proc_anterior = p_proc_actual;
    p_proc_actual = planificador_prioridad();
    if (p_proc_actual != NULL) {
        printk("->C.CONTEXTO POR FIN: de %d a %d\n", p_proc_anterior->id, p_proc_actual->id);
        cambio_contexto(&(p_proc_anterior->contexto_regs), &(p_proc_actual->contexto_regs));
    } else {
        printk("->C.CONTEXTO POR FIN: de %d a NULO\n", p_proc_anterior->id);
        cambio_contexto(&(p_proc_anterior->contexto_regs), NULL);
        while (p_proc_actual == NULL) {
            espera_int();
        }
    }
    
    return -1;
}

/*
 * Tratamiento de llamada al sistema crear_proceso_prioridad. Llama a la
 * funcion auxiliar crear_tarea_prioridad
 */
int sis_crear_proceso_prioridad() {
    char *prog;
    int prioridad;
    int res;
    
    prog = (char *) leer_registro(1);
    prioridad = (int) leer_registro(2);
    res = crear_tarea_prioridad(prog, prioridad);
    printk("->PROC %d: CREAR PROCESO DE TIPO %s CON PRIORIDAD %d\n", p_proc_actual->id, prog, prioridad);
    return res;
}
/*
 *Llamada al sistema que realiza la creacion de un mutex
 */
int sis_crear_mutex() {
    char *nombre;
    int tipo;
    int posicion;
    Mutex nuevo_mutex;
    
    //Capturo los parametros de la llamada al sistema
    nombre = (char *) leer_registro(1);
    tipo = (int) leer_registro(2);
    
    
    //Compruebo que el tipo pasado es el correcto
    if (tipo > 1 || tipo < 0) {
        printk("->ERROR TIPO DEL MUTEX INCORRECTO\n");
        return -1;
    } else {
        
        if ((posicion = buscar_entrada_mutex_libre(nombre)) == -1) {
            printk("->YA EXISTE UN MUTEX CON ESE NOMBRE\n");
            return -1;
        } else if (strlen(nombre) > 8) {
            printk("->ERROR NOMBRE INTRODUCIDO CON MAS DE 8 CARACTERES\n");
            return -1;
        } else if (posicion >= NUM_MUT) {
            printk("->ERROR NUMERO MAXIMO DE MUTEX ALCANZADO\n");
            return -1;
        } else {
            
            strcpy(nuevo_mutex.nombre_mutex, nombre);
            
            nuevo_mutex.estado = M_UNLOCK;
            nuevo_mutex.id_mutex = posicion;
            nuevo_mutex.id_proceso_bloqueante = -1;
            nuevo_mutex.tipo_mutex = tipo;
            tabla_mutex[posicion] = nuevo_mutex;
            
            numero_mutex++;
            
            //inicializo la lista de bloqueados del mutex
            lista_bloqueados_mutex[posicion].primero = NULL;
            lista_bloqueados_mutex[posicion].ultimo = NULL;
            
            printk("->MUTEX (%s) CREADO POR EL PROCESO (%d)\n", nombre, p_proc_actual->id);
        }
    }
    
    //imprimir_mutex();
    return nuevo_mutex.id_mutex;
}

/*
 * llamada al sistema que realiza la apertura de un mutex
 */
int sis_abrir_mutex() {
    //printk("voy a ejecutar sis_abrir_mutex\n");
    char *nombre = (char *) leer_registro(1);
    int indice_for = 0;
    
    for (; indice_for < numero_mutex; indice_for++) {
        if (strcmp(nombre, tabla_mutex[indice_for].nombre_mutex) == 0) {
            
            if (!comprobarMutexAbierto(p_proc_actual, tabla_mutex[indice_for].id_mutex)){
                if(anyadirMutexAbierto(p_proc_actual, tabla_mutex[indice_for].id_mutex)) {
                    printk("->MUTEX (%s) ABIERTO PoR EL PROCESO (%d)\n", tabla_mutex[indice_for].nombre_mutex, p_proc_actual->id);
                }else{
                    return -1;
                }
                
            }else{
                printk("->EL MUTEX (%s) YA LO TIENE ABIERTO EL PROCESO\n", tabla_mutex[indice_for].nombre_mutex);
                
            }
            return tabla_mutex[indice_for].id_mutex;
            
        }
    }
    
    return -1;
}

/*Llamada al sistema que realiza la peticion de bloqueo de un mutex*/
int sis_lock() {
    int id = (int) leer_registro(1);
    BCP * p_proc_anterior;
    
    if (id >= numero_mutex) {
        printk("->PROCESO (%d) INTENTA HACER UN LOCK AL DESCRIPTOR (%d) QUE NO EXISTE\n", p_proc_actual->id, id);
        return -1;
    } else if(comprobarMutexAbierto(p_proc_actual, id)){
        if (tabla_mutex[id].estado != LOCK) {
            printk("->EL PROCESO (%d) HA BLOQUEADO (%s)\n", p_proc_actual->id, tabla_mutex[id].nombre_mutex);
            tabla_mutex[id].estado = LOCK;
            tabla_mutex[id].id_proceso_bloqueante = p_proc_actual->id;
        } else {
            if (p_proc_actual->id != tabla_mutex[id].id_proceso_bloqueante) {
                printk("->EL PROCESO (%d) INTENTA BLOQUEAR EL MUTEX (%s) QUE YA ESTA BLOQUEADO\n->Pasa a la cola de bloqueados del mutex\n", p_proc_actual->id, tabla_mutex[id].nombre_mutex);
                p_proc_actual->estado = BLOQUEADO;
                insertar_ultimo(&lista_bloqueados_mutex[id], p_proc_actual);
                imprimir_listas_mutex();
                p_proc_anterior = p_proc_actual;
                p_proc_actual = planificador_prioridad();
                numero_ticks_proceso = 0;
                if (p_proc_actual != NULL) {
                    printk("->C.CONTEXTO POR FIN: de %d a %d\n", p_proc_anterior->id, p_proc_actual->id);
                    cambio_contexto(&(p_proc_anterior->contexto_regs), &(p_proc_actual->contexto_regs));
                } else {
                    printk("->C.CONTEXTO POR FIN: de %d a NULO\n", p_proc_anterior->id);
                    cambio_contexto(&(p_proc_anterior->contexto_regs), NULL);
                    while (p_proc_actual == NULL) {
                        espera_int();
                    }
                }
            }else if(tabla_mutex[id].tipo_mutex == RECURSIVO && p_proc_actual->id == tabla_mutex[id].id_proceso_bloqueante) {
                tabla_mutex[id].num_bloqueos++;
                printk("->EL PROCESO (%d) BLOQUEA EL MUTEX RECURSIVO (%s) %d VECES.\n", p_proc_actual->id, tabla_mutex[id].nombre_mutex, tabla_mutex[id].num_bloqueos);
            } else {
                printk("->MUTEX NO RECURSIVO Y YA BLOQUEADO POR (%d)\n", tabla_mutex[id].id_proceso_bloqueante);
                return -1;
            }
            
        }
    }else {
        printk("->MUTEX (%s) NO ABIERTO POR EL PROCESO (%d)\n", tabla_mutex[id].nombre_mutex, p_proc_actual->id);
        return -1;
    }
    return id;
}

/**/
int sis_unlock() {
    int id = (int) leer_registro(1);
    BCP * p_proc_anterior;
    // BCP * procesoAux = NULL; /*variable auxiliar para comprobar si hay algun proceso para desbloquear*/
    //BCP * procesoSiguiente = NULL; /*variable auxiliar 2 para comprobar si hay algun proceso para desbloquear*/
    
    if (tabla_mutex[id].estado != LOCK) {
        printk("->EL MUTEX (%s) ya está en estado UNLOCK", tabla_mutex[id].nombre_mutex);
    } else if (tabla_mutex[id].id_proceso_bloqueante==p_proc_actual->id){
        if (tabla_mutex[id].tipo_mutex == RECURSIVO && tabla_mutex[id].num_bloqueos > 1){
            tabla_mutex[id].num_bloqueos--;
            printk("->EL PROCESO (%d) DESBLOQUEA EL MUTEX RECURSIVO (%s). QUEDAN %d BLOQUEOS.\n", p_proc_actual->id, tabla_mutex[id].nombre_mutex, tabla_mutex[id].num_bloqueos);
        }else if(lista_bloqueados_mutex[id].primero != NULL) {
            printk("->EL PROCESO (%d) DESBLOQUEA EL MUTEX (%s).\n", p_proc_actual->id, tabla_mutex[id].nombre_mutex);
            tabla_mutex[id].num_bloqueos=1;
            tabla_mutex[id].id_proceso_bloqueante = lista_bloqueados_mutex[id].primero->id;
            p_proc_actual->estado = BLOQUEADO;
            lista_bloqueados_mutex[id].primero->estado = LISTO;
            insertar_cola_prioridad(p_proc_actual);
            insertar_cola_prioridad(obtener_primero(&lista_bloqueados_mutex[id]));
            
            //Modificar la cola de bloqueados
            /* procesoAux = lista_bloqueados_mutex[id].primero;
             * procesoSiguiente = procesoAux->siguiente;
             * lista_bloqueados_mutex[id].primero = procesoSiguiente;
             * if (lista_bloqueados_mutex[id].primero == NULL)
             * lista_bloqueados_mutex[id].ultimo = NULL;*/
            
            p_proc_anterior = p_proc_actual;
            p_proc_actual = planificador_prioridad();
            
            //Si el planificador devulve el mismo proceso que el actual no se cambia de contexto ni se resetean los ticks por proceso
            if(p_proc_anterior->id!=p_proc_actual->id) {
                numero_ticks_proceso = 0;
                if (p_proc_actual != NULL) {
                    printk("->C.CONTEXTO POR FIN: de %d a %d\n", p_proc_anterior->id, p_proc_actual->id);
                    
                    cambio_contexto(&(p_proc_anterior->contexto_regs), &(p_proc_actual->contexto_regs));
                } else {
                    printk("->C.CONTEXTO POR FIN: de %d a NULO\n", p_proc_anterior->id);
                    cambio_contexto(&(p_proc_anterior->contexto_regs), NULL);
                    
                    while (p_proc_actual == NULL) {
                        espera_int();
                    }
                }
            }
            //imprimir_listas_mutex();
            
        } else {
            tabla_mutex[id].id_proceso_bloqueante = -1;
            tabla_mutex[id].estado = UNLOCK;
            tabla_mutex[id].num_bloqueos=0;
        }
        
    }else{printk("->EL MUTEX (%s) NO HA SIDO BLOQUEADO POR EL PROCESO (%d)", tabla_mutex[id].nombre_mutex, p_proc_actual->id);}
    return id;
}

/*
 * llamada al sistema que realiza el cierre de un mutex abierto
 */
int sis_cerrar_mutex() {
    int id = (int) leer_registro(1);
    if(isMutexExiste(id)){
        if (comprobarMutexAbierto(p_proc_actual, id)){
            if (eliminarMutexAbierto(p_proc_actual, id)){
                liberarMutex(id);
                printk("Mutex %s cerrado\n", tabla_mutex[id].nombre_mutex);
                return 1;
            }
        }else{
            printk("Mutex %s no ha sido abierto por el proceso %d\n", tabla_mutex[id].nombre_mutex, p_proc_actual->id);
        }
    }else{
        printk("El mutex con el descriptor %d no existe\n", id);
    }
    
    return -1;
}

/*
 * Llamada al sistema que permite leer un caracter por el terminal
 */
int sis_leer_caracter() {
    
    BCP * p_proc_anterior;
    char caracter;
    if (numero_caracteres == 0) {
        p_proc_actual->estado = BLOQUEADO;
        insertar_ultimo(&lista_bloqueados_teclado, p_proc_actual);
        p_proc_anterior = p_proc_actual;
        p_proc_actual = planificador_prioridad();
        // imprimir_listas();
        if (p_proc_actual != NULL) {
            printk("->C.CONTEXTO POR FIN: de %d a %d\n", p_proc_anterior->id, p_proc_actual->id);
            cambio_contexto(&(p_proc_anterior->contexto_regs), &(p_proc_actual->contexto_regs));
        } else {
            printk("->C.CONTEXTO POR FIN: de %d a NULO\n", p_proc_anterior->id);
            cambio_contexto(&(p_proc_anterior->contexto_regs), NULL);
            while (p_proc_actual == NULL) {
                espera_int();
            }
        }
    } else {
        caracter=buffer_teclado[0];
        numero_caracteres--;
        recolocarBuffer(buffer_teclado);
    }
    return caracter;
}
/*Desplaza el buffer una posicion a la izquierda*/
void recolocarBuffer(char * buffer) {
    if(numero_caracteres!=0) {
        int i=0;
        for(;i<numero_caracteres;i++)
            buffer[i]=buffer[i+1];
    }
}

/*
 *
 * Rutina de inicializaci�n invocada en arranque
 *
 */
int main() {
    //printk("voy a ejecutar main\n");
    numero_ticks_global = 0;
    /* se llega con las interrupciones prohibidas */
    iniciar_tabla_proc();
    inicializarMutex();
    instal_man_int(EXC_ARITM, exc_arit);
    instal_man_int(EXC_MEM, exc_mem);
    instal_man_int(INT_RELOJ, int_reloj);
    instal_man_int(INT_TERMINAL, int_terminal);
    instal_man_int(LLAM_SIS, tratar_llamsis);
    instal_man_int(INT_SW, int_sw);
    iniciar_cont_int(); /* inicia cont. interr. */
    iniciar_cont_reloj(TICK); /* fija frecuencia del reloj */
    iniciar_cont_teclado(); /* inici cont. teclado */
    expulsado_init = 0;
    numero_ticks_proceso = 0;
    /* crea proceso inicial */
    if (crear_tarea_prioridad((void *) "init", P_ALTO) < 0)
        panico("no encontrado el proceso inicial");
    
    /* activa proceso inicial */
    p_proc_actual = planificador_init();
    cambio_contexto(NULL, &(p_proc_actual->contexto_regs));
    panico("S.O. reactivado inesperadamente");
    return 0;
}
